package com.yuyou.fn.educational.service.impl;

import com.github.wens.framework.service.impl.SuperServiceImpl;
import com.github.wens.mybatisplus.examples.Example;
import com.github.wens.mybatisplus.plugins.Page;
import com.yuyou.fn.common.exception.BusException;
import com.yuyou.fn.common.util.BeanUtils;
import com.yuyou.fn.common.util.CriteriaUtils;
import com.yuyou.fn.common.util.TimeUtils;
import com.yuyou.fn.common.util.Utils;
import com.yuyou.fn.educational.entity.Period;
import com.yuyou.fn.educational.entity.PeriodSegment;
import com.yuyou.fn.educational.mapper.PeriodMapper;
import com.yuyou.fn.educational.service.IPeriodSegmentService;
import com.yuyou.fn.educational.service.IPeriodService;
import com.yuyou.fn.educational.vo.*;
import com.yuyou.fn.platform.permission.DataPermission;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

/**
 *
 * Period 表数据服务层接口实现类
 *
 */
@Service
public class PeriodServiceImpl extends SuperServiceImpl<PeriodMapper, Period,Long> implements IPeriodService {

    @Resource
    private IPeriodSegmentService periodSegmentService ;



    @Transactional
    @CacheEvict(value = {"CacheOfNearPeriod","CacheOfPeriod"},allEntries = true)
    @Override
    public void saveOrUpdatePeriod(PeriodSaveOrUpdateVo periodSaveOrUpdateVo, List<PeriodSegmentVo> segmentList) {

        Period period = new Period();

        BeanUtils.copyProperties(period,periodSaveOrUpdateVo);
        checkPeriod(period , segmentList );
        period.setStartDate(segmentList.get(0).getStartDate());
        period.setEndDate(segmentList.get(segmentList.size() - 1).getEndDate());
        if (StringUtils.isEmpty(period.getSequentialPeriodIds())){
            period.setSequentialPeriodIds("");
        }
        if (StringUtils.isEmpty(period.getStepPeriodIds())){
            period.setStepPeriodIds("");
        }
        if (StringUtils.isNotEmpty(period.getSequentialPeriodIds()) && StringUtils.isNotEmpty(period.getStepPeriodIds())){
            List<Long> sequentialPeriodIds= Utils.toLongIds(period.getSequentialPeriodIds());
            List<Long> stepPeriodIds=Utils.toLongIds(period.getStepPeriodIds());
            for (Long sequentialPeriod:sequentialPeriodIds){
                for (Long stepPeriodId:stepPeriodIds){
                    if (sequentialPeriod.equals(stepPeriodId)){
                        Period check=this.findById(stepPeriodId);
                        throw new BusException("期名为"+check.getPeriodName()+"不可同时为顺期来源和跨期来源");
                    }
                }
            }
        }

        if(period.getPeriodId() == null ){
            period.setUpdateTime(TimeUtils.now());
            period.setCreateTime(TimeUtils.now());
            period.setIsCurrent(0);
            Integer periodNo=1;
            Example<Period> periodExample  = Period.createExample();
            periodExample.createCriteria().andEqualTo("areaTeamId" , period.getAreaTeamId() );
            List<Period> periods=this.findListByExample(periodExample);
            for (Period period1:periods){
                if (periodNo<=period1.getPeriodNo()){
                    periodNo=period1.getPeriodNo()+1;
                }
            }
            period.setPeriodNo(periodNo);



          /*  if(period.getPeriodNo() != 1 ){
                Example<Period> example = Period.createExample();
                example.createCriteria().andEqualTo("areaTeamId" , period.getAreaTeamId() ).andEqualTo("periodNo" , period.getPeriodNo() - 1 );
                Period prePeriod =  findOneByExample(example);
                if(prePeriod == null ){
                    throw new BusException("期号不连续，" + ( period.getPeriodNo() -1 ) +"期不存在") ;
                }
            }*/
            insertSelective(period);
        }else{
            period.setUpdateTime(TimeUtils.now());
            updateSelectiveById(period);
        }
        periodSegmentService.updateSegment(period.getPeriodId(),segmentList);
    }

    private void checkPeriod(Period period, List<PeriodSegmentVo> segmentList) {
    /*    Example<Period> example = Period.createExample();
        example.createCriteria().andEqualTo("areaTeamId" ,period.getAreaTeamId() ).andEqualTo("periodNo",period.getPeriodNo());
        Period one = findOneByExample(example);
        if(one != null && ( period.getPeriodId() == null || !period.getPeriodId().equals(one.getPeriodId()) )){
            throw new BusException("期次重复");
        }*/

        for(int i = 0 ; i < segmentList.size() ;i++ ){
            PeriodSegmentVo vo = segmentList.get(i);
            if(vo.getStartDate().after(vo.getEndDate())){
                throw new BusException("段次" + (i + 1 ) + "开始日期大于结束日期") ;
            }
            if(i > 0 &&   vo.getStartDate().before(segmentList.get(i - 1).getEndDate())){
                throw new BusException("段次" + (i + 1 ) + "开始日期小于段次"+(i - 1)+"结束日期") ;
            }
        }
    }

    @Override
    public PeriodVo findPeriodByPeriodId(Long periodId) {
        Period period = findById(periodId);
        PeriodVo periodVo  = new PeriodVo() ;
        BeanUtils.copyProperties(periodVo,period);
        List<PeriodSegmentVo> segmentList = periodSegmentService.findPeriodSegmentList(periodId);
        periodVo.setSegmentList(segmentList);
        return periodVo ;


    }

    @Override
    public Page<Period> findPeriodForPage(Long areaTeamId, DataPermission dataPermission, int pageNo, int pageSize) {
        Example<Period> example = Period.createExample();
        example.orderBy("periodNo").desc() ;
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("areaTeamId" , areaTeamId );
        CriteriaUtils.fillConditionForAreaTeamPermission(criteria,dataPermission.getAreaTeamIds());
        return findPageByExample(example,pageNo,pageSize);
    }


    @Cacheable("CacheOfCurrentPeriod")
    @Override
    public Period findCurrentPeriod(Long areaTeamId ) {
        Example<Period> example = Period.createExample();
        example.createCriteria().andEqualTo("areaTeamId" , areaTeamId ).andEqualTo("isCurrent" , 1);
        return findOneByExample(example);
    }

    @CacheEvict(value = { "CacheOfNearPeriod" ,"CacheOfCurrentPeriod","CacheOfPeriod" } , allEntries = true )
    @Override
    @Transactional
    public void updateCurrentPeriod(Long periodId) {

        Period period = findById(periodId);

        Example<Period> example = Period.createExample();
        example.createCriteria().andEqualTo("areaTeamId",period.getAreaTeamId()).andEqualTo("isCurrent" , 1);

        Period update  = new Period();

        update.setIsCurrent(0);
        updateSelectiveByExample(update,example);

        update.setPeriodId(periodId);
        update.setIsCurrent(1);
        updateSelectiveById(update);

    }

    @Override
    public Period findPrePeriod(Long periodId) {
        Period period = findById(periodId);
        return findPrePeriod(period.getAreaTeamId(),period.getPeriodNo());
    }


    @Override
    public Period findPeriodByTime(Long areaTeamId, Date time) {
        Example<Period> example=Period.createExample();
        Example.Criteria criteria=example.createCriteria();
        criteria.andEqualTo("areaTeamId", areaTeamId);
        criteria.andLessThanOrEqualTo("startDate",time);
        criteria.andGreaterThanOrEqualTo("endDate",time);
        example.orderBy("periodNo").desc();
        return this.findOneByExample(example);
    }

    @Override
    public Period findPrePeriod(Long areaTeamId, Integer periodNo ) {
        Example<Period> example = Period.createExample();
        example.createCriteria().andEqualTo("areaTeamId" , areaTeamId ).andLessThan("periodNo" , periodNo );
        example.orderBy("createTime").desc();
        return findOneByExample(example);
    }

    @Override
    public Period findNextPeriod(Long periodId) {
        Period period = findById(periodId);
        Example<Period> example = Period.createExample();
        example.createCriteria().andEqualTo("areaTeamId" , period.getAreaTeamId() ).andGreaterThan("periodNo" , period.getPeriodNo() );
        example.orderBy("periodNo").asc();
        return findOneByExample(example);
    }


    @Cacheable("CacheOfNearPeriod")
    @Override
    public List<Period> findNearPeriod(Long areaTeamId , int amount) {
        Period currentPeriod = findCurrentPeriod(areaTeamId);
        if (currentPeriod==null){
            throw new BusException("该区域下暂无当前期,请前往设置");
        }
        Example<Period> example = Period.createExample();
        example.orderBy("periodNo").desc();
        example.createCriteria().andEqualTo("areaTeamId" , areaTeamId ).andBetween("periodNo" , currentPeriod.getPeriodNo() - amount , currentPeriod.getPeriodNo() + 4 );
        return findListByExample(example);
    }

    @Cacheable("CacheOfFindPeriodByAreaTeamIdPeriodName")
    @Override
    public Period findPeriodByAreaTeamIdPeriodName(Long areaTeamId ,String periodName) {
        Example<Period> example = Period.createExample();
        example.createCriteria().andEqualTo("areaTeamId" ,areaTeamId ).andEqualTo("periodName", periodName);
        return findOneByExample(example);
    }

    @Override
    public List<Period> findByYearAndAreaTeamId(Long areaTeamId, String year) {
        Example<Period> example = Period.createExample();
        example.createCriteria().andEqualTo("areaTeamId" , areaTeamId ).andLike("startDate",year.trim()+"%");
        example.orderBy("periodNo");
        return findListByExample(example);
    }

    @Override
    public List<Period> findListBeforePeriodId(Long areaTeamId, Long periodId) {
        Integer periodNo=null;
        if (periodId!=null){
            Period period=this.findById(periodId);
            if (period!=null){
                periodNo=period.getPeriodNo();
            }
        }

        Example<Period> example = Period.createExample();
        Example.Criteria criteria=example.createCriteria();
        criteria.andEqualTo("areaTeamId",areaTeamId);
        if (periodNo!=null){
            criteria.andLessThan("periodNo",periodNo);
        }
        return findListByExample(example);
    }

    @Override
    public List<Period> findBySequentialId(Long areaTeamId, Long sequentialId) {
        Example<Period> example = Period.createExample();
        Example.Criteria criteria=example.createCriteria();
        criteria.andEqualTo("areaTeamId",areaTeamId);
        criteria.andLike("sequentialPeriodIds","%"+sequentialId+"%");
        return findListByExample(example);
    }

    @Override
    public List<Period> findByStepId(Long areaTeamId, Long stepId) {
        Example<Period> example = Period.createExample();
        Example.Criteria criteria=example.createCriteria();
        criteria.andEqualTo("areaTeamId",areaTeamId);
        criteria.andLike("stepPeriodIds","%"+stepId+"%");
        return findListByExample(example);
    }

    @Override
    public List<Period> findRecentPeriod(Long areaTeamId, int amount) {
        Period currentPeriod = findCurrentPeriod(areaTeamId);
        Example<Period> example = Period.createExample();
        example.limit(amount);
        example.orderBy("periodNo").desc();
        example.createCriteria().andEqualTo("areaTeamId",areaTeamId).andLessThanOrEqualTo("periodNo",currentPeriod.getPeriodNo());
        return this.findListByExample(example);
    }

    @Override
    public List<Period> findPreviousByPeriodId(Long periodId, int amount) {
        Period period = this.findById(periodId);
        Example<Period> example = Period.createExample();
        example.limit(amount);
        example.orderBy("periodNo").desc();
        example.createCriteria().andEqualTo("areaTeamId",period.getAreaTeamId()).andLessThan("periodNo",period.getPeriodNo());
        return this.findListByExample(example);
    }

    @Override
    public List<Period> findNextByPeriodId(Long periodId, int amount) {
        Period period = this.findById(periodId);
        Example<Period> example = Period.createExample();
        example.limit(amount);
        example.orderBy("periodNo");
        example.createCriteria().andEqualTo("areaTeamId",period.getAreaTeamId()).andGreaterThan("periodNo",period.getPeriodNo());
        return this.findListByExample(example);
    }

    @Override
    public List<Period> findAllCurrentPeriods() {
        Example<Period> example = Period.createExample();
        example.createCriteria().andEqualTo("isCurrent" , 1);
        return findListByExample(example);
    }

    @Override
    public List<Period> findByAreaTeamIdsAndNames(List<Long> areaTeamIds, List<String> periodNames) {
        if (areaTeamIds==null || areaTeamIds.size()<=0 || periodNames==null || periodNames.size()<=0){
            return Collections.EMPTY_LIST;
        }
        Example<Period> example = Period.createExample();
        Example.Criteria criteria=example.createCriteria();
        criteria.andIn("areaTeamId",areaTeamIds);
        criteria.andIn("periodName",periodNames);
        return findListByExample(example);
    }

    @Override
    public List<Period> findByAreaTeamIdsAndBetweenNos(Long areaTeamId, Integer start, Integer end) {
        Example<Period> example = Period.createExample();
        Example.Criteria criteria=example.createCriteria();
        criteria.andEqualTo("areaTeamId",areaTeamId);
        criteria.andGreaterThanOrEqualTo("periodNo",start);
        criteria.andLessThanOrEqualTo("periodNo",end);
        return findListByExample(example);
    }

    @Override
    public List<Period> findByIdsOrderByPeriodNo(List<Long> periodIds) {
        Example<Period> example = Period.createExample();
        Example.Criteria criteria=example.createCriteria();
        criteria.andIn("periodId",periodIds);
        example.orderBy("periodNo");
        return findListByExample(example);
    }

    @Override
    public Period findByAreaPeriodNo(Long areaTeamId, Integer periodNo) {
        Example<Period> example = Period.createExample();
        Example.Criteria criteria=example.createCriteria();
        criteria.andEqualTo("areaTeamId",areaTeamId);
        criteria.andEqualTo("periodNo",periodNo);
        return findOneByExample(example);
    }

    @Override
    public List<Period> findListByAreaTeamId(Long areaTeamId) {
        Example<Period> example = Period.createExample();
        Example.Criteria criteria=example.createCriteria();
        criteria.andEqualTo("areaTeamId",areaTeamId);
        return findListByExample(example);
    }

    @Override
    public Period findMaxPeriod(long areaTeamId) {
        Example<Period> example=Period.createExample();
        Example.Criteria criteria=example.createCriteria();
        criteria.andEqualTo("areaTeamId",areaTeamId);
        criteria.andCondition(" period_no = (SELECT DISTINCT MAX(period_no) from yyfn.period where area_team_id="+areaTeamId+")");
        return findOneByExample(example);
    }

    @Override
    public List<PeriodRelativeVo> findPeriodRelativeByPeriodIds(List<Long> periodIds) {
        List<Period> periodList = this.findByIds(periodIds);
        List<PeriodRelativeVo> periodRelativeVoList=new ArrayList<>();

        for (Period period : periodList) {
            Long periodId = period.getPeriodId();
            List<Period> seqPeriodList = this.findBySequentialId(period.getAreaTeamId(), periodId);
            List<Long> seqPeriodIds = BeanUtils.toList(seqPeriodList, "periodId");
            if (seqPeriodIds.size()>0){
                PeriodRelativeVo periodRelativeVo=new PeriodRelativeVo();
                periodRelativeVo.setPeriodId(periodId);
                periodRelativeVo.setRelativePeriodIds(seqPeriodIds);
                periodRelativeVo.setType(1);
                periodRelativeVoList.add(periodRelativeVo);
            }

            List<Period> stepPeriodList = this.findByStepId(period.getAreaTeamId(), periodId);
            List<Long> stepPeriodIds = BeanUtils.toList(stepPeriodList, "periodId");

            if (stepPeriodIds.size()>0){
                PeriodRelativeVo periodRelativeVo=new PeriodRelativeVo();
                periodRelativeVo.setPeriodId(periodId);
                periodRelativeVo.setRelativePeriodIds(stepPeriodIds);
                periodRelativeVo.setType(0);
                periodRelativeVoList.add(periodRelativeVo);
            }
        }
        return periodRelativeVoList;
    }

    @Override
    public List<Period> findCurrentPeriodsByAreaTeamIds(List<Long> areaTeamIds) {
        if (Utils.checkListEmpty(areaTeamIds)){
            return Collections.EMPTY_LIST;
        }
        Example<Period> example = Period.createExample();
        example.createCriteria().andEqualTo("isCurrent" , 1).andIn("areaTeamId",areaTeamIds);
        return findListByExample(example);
    }

    @Override
    @Cacheable(value = "CacheOfPeriod",key = "#areaTeamId")
    public Map<Long, Period> findPeriodMapByAreaTeamId(long areaTeamId) {
        Example<Period> example=Period.createExample();
        example.createCriteria().andEqualTo("areaTeamId",areaTeamId);
        List<Period> periodList = this.findListByExample(example);
        return BeanUtils.toMap(periodList,"periodId");
    }

    @Override
    public PeriodSeqStepSituationVo findPeriodSeqStepSituation(List<Long> periodIds) {
        List<Period> periodList = this.findByIds(periodIds, "periodId", "periodName");
        Map<Long,List<Long>> seqMap=new HashMap<>();
        Map<Long,List<Long>> stepMap=new HashMap<>();
        List<Long> seqStepList=new ArrayList<>();
        for (Period period:periodList){
            Long periodId=period.getPeriodId();
            List<Period> seqPeriodList = this.findBySequentialId(period.getAreaTeamId(), periodId);
            List<Period> stepPeriodList = this.findByStepId(period.getAreaTeamId(),periodId);
            List<Long> useSeqPeriodIds=BeanUtils.toList(seqPeriodList,"periodId");
            List<Long> useStepPeriodIds=BeanUtils.toList(stepPeriodList,"periodId");
            seqStepList.addAll(useSeqPeriodIds);
            seqStepList.addAll(useStepPeriodIds);
            seqMap.put(periodId,useSeqPeriodIds);
            stepMap.put(periodId,useStepPeriodIds);
        }
        List<Long> all=new ArrayList<>();
        all.addAll(periodIds);
        all.addAll(seqStepList);

        List<PeriodSegment> periodSegmentList=periodSegmentService.findByPeriodSegmentListByPeriodIds(all);

        Map<Long,Map<Integer,PeriodSegment>> periodSegmentMap=periodSegmentService.periodSegmentToMapGroup(periodSegmentList);

        PeriodSeqStepSituationVo periodSeqStepSituationVo=new PeriodSeqStepSituationVo();
        periodSeqStepSituationVo.setPeriodList(periodList);
        periodSeqStepSituationVo.setSeqStepList(seqStepList);
        periodSeqStepSituationVo.setSeqMap(seqMap);
        periodSeqStepSituationVo.setStepMap(stepMap);
        periodSeqStepSituationVo.setAllPeriodIds(all);
        periodSeqStepSituationVo.setPeriodSegmentMap(periodSegmentMap);
        return periodSeqStepSituationVo;
    }
}